﻿package com.snzflash.components.list 
{
	import com.snzflash.components.core.UIComponent;
	import com.snzflash.components.core.UIContainer;
	import com.snzflash.events.ComponentEvent;
	import flash.display.Sprite;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	
	/**
	 * Базовый класс для создания списков
	 * @author firsoff maxim, tel: +79222345364, skype: maxim.firsoff
	 */
	public class List extends UIComponent
	{
		
		// параметры скроллирования
		protected var _maxHorizontalScrollPosition:Number = 0;
		protected var _maxVerticalScrollPosition:Number = 0;
		protected var _horizontalScrollPosition:Number = 0;
		protected var _verticalScrollPosition:Number = 0;
		protected var _verticalScrollProgress:Number = 0;
		protected var _horizontalScrollProgress:Number = 0;
		// факт смены параметров пунктов
		protected var _itemsChanged:Boolean;
		protected var _propertiesChanged:Boolean;
		// отступы
		protected var _verticalGap:Number = 0;
		protected var _horizontalGap:Number = 0;
		
		// выделенный пункт ( индекс )
		protected var _selectedIndex:int = -1;
		// выделенный объект
		protected var _selectedItem:Object;
		// выделенный визуальный объект
		protected var _selectedItemRenderer:IListItemRenderer;
		// рендерер
		protected var _itemRenderer:Class = ListItemRenderer;
		// контейнер
		protected var _content:UIContainer;
		// провайдер данных
		protected var _dataProvider:Array;
		// массив хранитель всех пунктов
		protected var _items:/*IListItemRenderer*/Array;
		public function List() 
		{
			super();
		};
		
		
		override protected function create():void 
		{
			super.create();
			_content = new UIContainer();
			addChild(_content);
		};
		
		
		public function clear():void 
		{
			for each(var i:ListItemRenderer  in _items) 
			{
				// из контейнера удаляет CasaSprite
				i.destroy();
			};
			_items = new Array();
		};
		
		
		protected function build():void
		{
			////trace(this, _dataProvider);
			clear();
			_itemsChanged = true;
			_propertiesChanged = true;
			var item:ListItemRenderer;
			for each(var i:Object  in _dataProvider) 
			{
				item = new _itemRenderer();
				_content.addChild(item);
				item.addEventListener ( MouseEvent.CLICK, onItemClick);
				item.data = i;
				_items.push(item);
				
			};
			invalidate();
		};
		
		override public function destroy():void 
		{
			clear();
			super.destroy();
		};
		
		public function addItem(value:Object):void 
		{
			if ( !_dataProvider) _dataProvider = [];
			////trace('ADD ITEM : ' + this, _dataProvider, _items, _content, value);
			var item:ListItemRenderer = new _itemRenderer();
			item.data = value;
			_content.addChild(item);
			item.addEventListener ( MouseEvent.CLICK, onItemClick);
			_items.push(item);
			_dataProvider.push(value);
			_propertiesChanged = true;
			//updateItems();
			invalidate();
		};
		
		public function removeItem(value:Object):void 
		{
			if ( !_dataProvider) return;
			var ind:int = _dataProvider.indexOf(value)
			if ( ind == -1) return;
			_dataProvider.splice(ind, 1);
			
			for each(var i:ListItemRenderer  in _items) 
			{
				if ( i.data == value) 
				{
					i.removeEventListener( MouseEvent.CLICK, onItemClick);
					i.destroy();
					_items.splice(_items.indexOf(i), 1);
				};
			};
			_propertiesChanged = true;
			invalidate();
			//updateItems();
		};
		
		override protected function updateDisplayList():void 
		{
			//return
			super.updateDisplayList();	
			scrollRect = new Rectangle(0, 0, width, height);
			if (_itemsChanged) { updateItems(); };
			
			////trace(scrollRect, _content);
			var d_h:Number = _content.height - scrollRect.height
			var d_w:Number = _content.width - scrollRect.width;
			_maxVerticalScrollPosition = d_h < 0 ? 0 :d_h;
			_maxHorizontalScrollPosition =   d_w < 0 ? 0 :d_w;
			
			if ( _propertiesChanged) { updateProperties(); };
			 //диспатчим, что параметры обновились
			dispatchEvent( new ComponentEvent ( ComponentEvent.UPDATE_COMPLETE ) );
		};
		//
		//override protected function updateDisplayList():void 
		//{
			//super.updateDisplayList();
		//};
		
		/**
		 * обновление раскладки пунктов
		 */
		protected function updateItems():void
		{
			////trace('SUPER updateItems: ');
			_itemsChanged = false;
		};
		
		/**
		 * Обновление свойств листа
		 */
		protected function updateProperties():void
		{
			
			scrollRect = new Rectangle (0, 0, __width, __height);
			////trace('updateProperties : ' + _verticalScrollPosition);
			verticalScrollPosition = _verticalScrollPosition;
			horizontalScrollPosition = _horizontalScrollPosition;
			
			_content.x = -horizontalScrollPosition;
			_content.y = -verticalScrollPosition;
			
			_propertiesChanged = false;
		};
		
		override public function invalidate():void 
		{
			////trace();
			super.invalidate();
			_itemsChanged = true;
		};
		
		override public function get height():Number { return super.height; };
		
		override public function set height(value:Number):void 
		{
			super.height = value;
			_propertiesChanged = true;
		};
		
		
		override public function get width():Number { return super.width; };
		
		override public function set width(value:Number):void 
		{
			super.width = value;
			_propertiesChanged = true;
		};
		//override public function validateNow():void 
		//{
			////trace('validateNow : ' + _itemsChanged);
			//if (_itemsChanged) updateItems();
			//super.validateNow();
		//};
		/**
		 * Максимальная позиция для скроллирования
		 */
		public function get maxHorizontalScrollPosition():Number { return _maxHorizontalScrollPosition; };
		
		public function get maxVerticalScrollPosition():Number { return _maxVerticalScrollPosition; };
		
		/**
		 * Текущая позиция скролла
		 */
		public function get horizontalScrollPosition():Number { return _horizontalScrollPosition; };
		
		public function set horizontalScrollPosition(value:Number):void 
		{
			
			value = value < 0 ? 0 : value;
			value = value > maxHorizontalScrollPosition ? maxHorizontalScrollPosition : value;
			_horizontalScrollPosition = value;
			////trace('SET : ' + maxHorizontalScrollPosition,_horizontalScrollPosition);
			_horizontalScrollProgress = maxHorizontalScrollPosition ? value / maxHorizontalScrollPosition : maxHorizontalScrollPosition;
			_propertiesChanged = true;
			invalidate();
		};
		
		public function get verticalScrollPosition():Number { return _verticalScrollPosition; };
		
		public function set verticalScrollPosition(value:Number):void 
		{
		
			value = value < 0 ? 0 : value;
			value = value > maxVerticalScrollPosition ? maxVerticalScrollPosition : value;
			////trace('verticalScrollPosition : ' + value, maxVerticalScrollPosition);
			_verticalScrollPosition = value;
			_verticalScrollProgress =_maxVerticalScrollPosition ?  value / _maxVerticalScrollPosition : _maxVerticalScrollPosition;
			_propertiesChanged = true;
			invalidate();
		};
		
		/**
		 * Вертикальный отступ
		 */
		public function get verticalGap():Number { return _verticalGap; };
		
		public function set verticalGap(value:Number):void 
		{
			_verticalGap = value;
			_itemsChanged = true;
			invalidate();
		};
		
		
		override public function validateNow():void 
		{
			////trace(this + ' validateNow '  + _itemsChanged);
			super.validateNow();
			if (_propertiesChanged) updateProperties();
			if ( _itemsChanged ) updateItems();
		};
		/**
		 * Горизонтальный отступ
		 */
		public function get horizontalGap():Number { return _horizontalGap; };
		
		public function set horizontalGap(value:Number):void 
		{
			_horizontalGap = value;
			_itemsChanged = true;
			invalidate();
		};
		
		/**
		 * Контейнер с содержимым
		 */
		public function get content():Sprite { return _content; };
		
		public function get selectedIndex():int { return _selectedIndex; };
		
		public function set selectedIndex(value:int):void 
		{
			var oldVAlue:int = _selectedIndex;
			if (_selectedItemRenderer) _selectedItemRenderer.selected = false;
			_selectedItem = null;
			_selectedItemRenderer = null;
			if (value > (_items.length - 1) ) throw new IllegalOperationError ( 'selectedIndex - out of the range : [' + value  + '] in : ' + this);
			if (value < 0) { _selectedIndex = -1; return; };			
			selectedItem = _items[value].data;
			
			if (!_selectedItemRenderer) _selectedIndex = oldVAlue;
			
		};
		
		public function get selectedItem():Object { return _selectedItem; };
		
		public function set selectedItem(value:Object):void 
		{
			_selectedItem = value;
			if (_selectedItemRenderer) _selectedItemRenderer.selected = false;
			_selectedItemRenderer = null;
			for each(var i:IListItemRenderer  in _items) 
			{
				////trace(i.data ,value);
				if (i.data == value) 
				{
					_selectedItem = i.data; 
					_selectedItemRenderer = i;
					_selectedItemRenderer.selected = true;
					_selectedIndex = _items.indexOf(i);
					break;
				};
			};
		};
		
		protected function get selectedItemRenderer():IListItemRenderer { return _selectedItemRenderer; };
		
		protected function set selectedItemRenderer(value:IListItemRenderer):void 
		{
			if (_selectedItemRenderer) _selectedItemRenderer.selected = false;
			_selectedItemRenderer = value;
			if ( !value ) 
			{
				_selectedItem = null;
				_selectedIndex = -1;
				return;
			}
			_selectedItemRenderer.selected = true;
			_selectedItem = _selectedItemRenderer.data;
			_selectedIndex = _items.indexOf(_selectedItemRenderer);
		};
		
		public function get itemRenderer():Class { return _itemRenderer; };
		
		public function set itemRenderer(value:Class):void 
		{
			_itemRenderer = value;
			build();
		};
		
		
		public function get dataProvider():Array { return _dataProvider; };
		
		public function set dataProvider(value:Array):void 
		{
			////trace('dataProvider : ' + value.length);
			_dataProvider = value;
			_selectedItemRenderer = null;
			build();
			_verticalScrollPosition = 0;
			updateItems();
		};
		
		/**
		 * Установка скролла в долях от 0 до 1
		 */
		public function get verticalScrollProgress():Number { return _verticalScrollProgress; };
		
		public function set verticalScrollProgress(value:Number):void 
		{
			////trace(value);
			//value = value < 0 ? 0 : value > 1 ? 1  : value;
			//_verticalScrollProgress = value;
			//verticalScrollPosition = value * maxVerticalScrollPosition;
			
			value = value > 1 ? 1 : value;
			value = value < 0 ? 0 : value;
			_verticalScrollProgress = value;
			verticalScrollPosition = _maxVerticalScrollPosition * value;
		};
		
		public function get horizontalScrollProgress():Number { return _horizontalScrollProgress; };
		
		public function set horizontalScrollProgress(value:Number):void 
		{
			//value = value < 0 ? 0 :  value > 1 ? 1  : value;
			//_horizontalScrollProgress = value;
			//horizontalScrollPosition = value * _maxHorizontalScrollPosition;
			
			value = value > 1 ? 1 : 0;
			value = value < 0 ? 0 : 0;
			_horizontalScrollProgress = value;
			horizontalScrollPosition = _maxHorizontalScrollPosition * value;
		};
		
		
		protected function onItemClick(event:MouseEvent):void 
		{
			////trace('@@@@@@@');
			var target:IListItemRenderer = IListItemRenderer ( event.currentTarget ) ;
			selectedItemRenderer = target;
			invalidate();
			//selectedItem = target.data;
			dispatchEvent ( new Event ( Event.CHANGE) );
		};	
		
		override public function get rawHeight():Number { /*//trace(_content.getBounds(this)); */return _content.height; };
		
		override public function set rawHeight(value:Number):void 
		{
			_content.height = value;
		};
		
		override public function get rawWidth():Number { return _content.width; };
		
		override public function set rawWidth(value:Number):void 
		{
			_content.width = value;
		};
		
		public function get items():/*IListItemRenderer*/Array { return _items; };
		
		
		
	}

}